/****************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the examples of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial Usage
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights.  These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU General Public License version 3.0 requirements will be
** met: http://www.gnu.org/copyleft/gpl.html.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
** $QT_END_LICENSE$
**
****************************************************************************/
#include <iostream>
#include <string>
#include <cmath>
#include <QtGui>

#include "renderarea.h"



//! [0]
RenderArea::RenderArea(QWidget *parent)
    : QGLWidget(parent)
{


    BoundingBox[0][0] = 1e10;
    BoundingBox[0][1] = 1e10;
    BoundingBox[1][0] = -1e10;
    BoundingBox[1][1] = -1e10;

//    Ite = 0;

    viewmod = Wired;
    PlotMode = false;
    //aPolygon = NULL;
    iniPolygon = NULL;
//    isStop = false;
    DataReady = false;
    this->isShowPoint = false;
    this->isShowOriginal = false;
    renderlog.open("rlog.txt");
    isShowLabelling = false;


}
//! [0]

RenderArea::~RenderArea(){
    //delete aPolygon;
    delete iniPolygon;
}

//! [1]
QSize RenderArea::minimumSizeHint() const
{
    return QSize(100, 100);
}
//! [1]

//! [2]
QSize RenderArea::sizeHint() const
{
    return QSize(400, 200);
}



bool RenderArea::loadFile(const QString &fileName)
{


    std::ifstream fpoly(fileName.toLatin1());
    if (!fpoly.good()) {
        return false;
    }

    PlotMode = false;
    char objLine[256];

    if(!Polygons.empty()) Polygons.clear();
    if(!Segments.empty()) Segments.clear();
    if(iniPolygon) delete iniPolygon;

//    Polygons.resize(1);

//    if(Polygons.empty())
//        return false;

    int Type = -1; // 0: polygon; 1 segments; -1 description
    int cnt = 0; // count the number of polygons

    double xmin = 1e10;
    double xmax = -1e10;
    double ymin = 1e10;
    double ymax = -1e10;
    MPolygon tmp;
    while (!fpoly.eof()){
        double px = 0.0, py = 0.0;
        double qx = 0.0, qy = 0.0;
        fpoly.getline( objLine, 255 );
        std::stringstream ss;
        std::string chkstr;
        ss << objLine;
        //ss>>chkstr;

        if(ss.str().compare("Polygons:")==0){
            Type = 0;
            continue;
        }
        if(ss.str().compare("Segments:")==0){
            Type = 1;
            continue;
        }

        if(Type == 0){

            if(ss.good()&& ss.str()!= ""){

                ss>>px;
                std::string str = ss.str().substr(ss.tellg(), ss.str().length() - ss.tellg()+1);
                int pos = str.find_first_not_of(" \t,");
                ss.seekg(ss.tellg()+pos);
                ss>>py;

                xmin = std::min(xmin, px);
                xmax = std::max(xmax, px);
                ymin = std::min(ymin, py);
                ymax = std::max(ymax, py);

                Point2d apt(px, py);
                tmp.points.push_back(apt);
            }
            if(ss.str()== ""){
                if(tmp.points.size()>0){
                    Polygons.push_back(tmp);
                    tmp.points.clear();
                }
            }
        }

        if(Type == 1){
            if(ss.good()&& ss.str()!= ""){
                ss>>px;
                std::string str = ss.str().substr(ss.tellg(), ss.str().length() - ss.tellg()+1);
                int pos = str.find_first_not_of(" \t,");
                ss.seekg(ss.tellg()+pos);
                ss>>py;
                str = ss.str().substr(ss.tellg(), ss.str().length() - ss.tellg()+1);
                pos = str.find_first_not_of(" \t,");
                ss.seekg(ss.tellg()+pos);
                ss>>qx;
                str = ss.str().substr(ss.tellg(), ss.str().length() - ss.tellg()+1);
                pos = str.find_first_not_of(" \t,");
                ss.seekg(ss.tellg()+pos);
                ss>>qy;

                xmin = std::min(xmin, px);
                xmax = std::max(xmax, px);
                ymin = std::min(ymin, py);
                ymax = std::max(ymax, py);

                xmin = std::min(xmin, qx);
                xmax = std::max(xmax, qx);
                ymin = std::min(ymin, qy);
                ymax = std::max(ymax, qy);

                Point2d p1(px, py);
                Point2d p2(qx, qy);
                Segments.push_back(Segment2d(p1, p2));
            }
        }

    }

    if(Polygons[0].points.size() < 2)
        return false;


    BoundingBox[0][0] = xmin;
    BoundingBox[1][0] = xmax;
    BoundingBox[0][1] = ymin;
    BoundingBox[1][1] = ymax;

    iniPolygon = new MPolygon;

    for(int i = 0; i<Polygons[0].PointNo(); i++){
        iniPolygon->points.push_back(Polygons[0].points[i]);
    }

    double color[3];
    color[0] = 0.8; color[1] = 0.2; color[2] = 0.1;
    draw_polygon(Polygons, color);

    updateGL();
    resizeGL(this->width(), this->height());

//    std::cout<<xmin<<", "<<xmax<<std::endl;
//    std::cout<<ymin<<", "<<ymax<<std::endl;
    DataReady = true;
    return true;
}

void RenderArea::initializeGL()
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glShadeModel (GL_SMOOTH);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();


}

void RenderArea::paintGL()
{

    glClearColor(0.0, 0.0, 0.0, 0.0);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    double color[3];
    color[0] = 0.8; color[1] = 0.3; color[2] = 0.3;
    draw_polygon(Polygons, color);
    if(this->isShowOriginal == true)
    {
        double color2[3];
        color2[0] = 0.1; color2[1] = 0.9; color2[2] = 0.1;
        std::vector<MPolygon> ipoly(1,*iniPolygon);
        draw_polygon(ipoly, color2);
    }

    if(this->isShowPoint == true && !Polygons.empty())
    {
        glPointSize(3.0);
        glColor3f(0.5, 0.9, 0.5);
        glBegin(GL_POINTS);
        for(int k = 0; k < Polygons.size(); k++){
            for(int i = 0; i < Polygons[k].PointNo(); i++){
                glVertex2d(Polygons[k].points[i].x, Polygons[k].points[i].y);
            }
        }
        glEnd();
    }
    draw_segments(Segments,color);
    draw_text();


}

void RenderArea::draw_text()
{
    glColor3f(0.8,1.0,0.5);
//    this->renderText(20, 20, tr("%1").arg(this->lastMouseX));
//    this->renderText(50, 20, tr("%1").arg(this->lastMouseY));
//    this->renderText(20, 40, tr("Ratio: %1").arg(ratio));
    if(!Polygons.empty()){
        this->renderText(20, 20, tr("Polygon Num.: %1").arg(this->Polygons.size()));
        this->renderText(20, 40, tr("Point Num.: %1").arg(this->getVerticesNumber()));
    }
    else
        this->renderText(20, 40, tr("Point Num.: %1").arg(0));
    this->renderText(20, 60, tr("xmin: %1; xmax: %2").arg(this->BoundingBox[0][0]).arg(this->BoundingBox[1][0]));
    this->renderText(20, 80, tr("ymin: %1; ymax: %2").arg(this->BoundingBox[0][1]).arg(this->BoundingBox[1][1]));


    coorX = minX + ((double)this->lastMouseX / width())*(maxX - minX);
    coorY = minY + (1.0 - (double)this->lastMouseY / height())*(maxY - minY);
    this->renderText(20, 100, tr("%1, %2").arg(coorX).arg(coorY));

    if(isShowLabelling == true){
        if(!this->Polygons.empty()){
            for(int k = 0; k < Polygons.size(); k++){
                for(int i = 0; i < Polygons[k].PointNo(); i++){
                    int posx = (int)width()*(this->Polygons[k].points[i].x - minX) / (maxX-minX);
                    int posy = (int)height()*(1.0-(this->Polygons[k].points[i].y - minY) / (maxY-minY));
                    this->renderText(posx, posy, tr("%1").arg(i));
                }
            }
        }
    }

}


void RenderArea::resizeGL(int w, int h)
{

    GLdouble xRange = BoundingBox[1][0] - BoundingBox[0][0];
    GLdouble yRange = BoundingBox[1][1] - BoundingBox[0][1];

    GLdouble xmin = BoundingBox[0][0];
    GLdouble xmax = BoundingBox[1][0];
    GLdouble ymin = BoundingBox[0][1];
    GLdouble ymax = BoundingBox[1][1];

    GLdouble leng = sqrt(xRange*xRange + yRange*yRange )/8.0;



    if(h == 0)
        h = 1;
    // Set Viewport to window dimensions
    glViewport(0, 0, w, h);
    // Reset projection matrix stack
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    if(PlotMode == true){
        glOrtho (-1.0, 1.0,
                 -1.0, 1.0,
                 -1.0, 1.0);

        minX = -1.0; maxX = 1.0;
        minY = -1.0; maxY = 1.0;
    }
    else{
    // Establish clipping volume (left, right, bottom, top, near, far)
    GLdouble lenx = xmax - xmin + leng;
    GLdouble leny = ymax - ymin + leng;
    if (w <= h){
        glOrtho (xmin - leng/2.0, xmax + leng/2.0,
                 (-leny/2.0)*h/w + (ymin+ymax)/2.0, (leny/2.0)*h/w + (ymin+ymax)/2.0,
                 -1.0, 1.0);

        minX = xmin - leng/2.0;
        maxX = xmax + leng/2.0;
        minY = (-leny/2.0)*h/w + (ymin+ymax)/2.0;
        maxY = (leny/2.0)*h/w + (ymin+ymax)/2.0;
    }
    else{
        glOrtho ((-lenx/2.0)*w/h + (xmin+xmax)/2.0, (lenx/2.0)*w/h + (xmin+xmax)/2.0,
                 (ymin - leng/2.0), (ymax + leng/2.0),
                 -1.0, 1.0);

        minX = (-lenx/2.0)*w/h + (xmin+xmax)/2.0;
        maxX = (lenx/2.0)*w/h + (xmin+xmax)/2.0;
        minY = (ymin - leng/2.0);
        maxY = (ymax + leng/2.0);
    }

    }

    // Reset Model view matrix stack
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}


void RenderArea::draw_polygon(std::vector<MPolygon> &polygons, double * color)
{


    if(polygons.size()<1)
        return;

    if(viewmod == Filled){
        glColor3f(0.5, 0.5, 0.8);
        glEnable(GL_LINE_SMOOTH);
        glEnable(GL_POLYGON_SMOOTH);
        for(int k = 0; k < polygons.size(); k++){
            glBegin(GL_POLYGON);
            for(int i = 0; i < polygons[k].PointNo(); i++)
                glVertex2d(polygons[k].points[i].x, polygons[k].points[i].y);
            glEnd();
        }
    }

    if(viewmod == Wired){

            glEnable(GL_LINE_SMOOTH);
            glColor3f(color[0], color[1], color[2]);
        for(int k = 0; k < polygons.size(); k++){
            glBegin(GL_LINES);

                for(int i = 0; i < polygons[k].PointNo() - 1; i++){
                    glVertex2d(polygons[k].points[i].x, polygons[k].points[i].y);
                    glVertex2d(polygons[k].points[i+1].x, polygons[k].points[i+1].y);
                }


                glVertex2d(polygons[k].points[polygons[k].PointNo() - 1].x, polygons[k].points[polygons[k].PointNo() - 1].y);
                glVertex2d(polygons[k].points[0].x, polygons[k].points[0].y);


            glEnd();

        }
    }

//    updateGL();
}


void RenderArea::draw_segments(std::vector<Segment2d> &Segments, double * color){

    glEnable(GL_LINE_SMOOTH);
    glColor3f(color[0], color[1], color[2]);

    for(int k = 0; k < Segments.size(); k++){
        glBegin(GL_LINES);
            glVertex2d(Segments[k].A.x, Segments[k].A.y);
            glVertex2d(Segments[k].B.x, Segments[k].B.y);
        glEnd();
    }
}

void RenderArea::mousePressEvent(QMouseEvent *e)
{

    if (PlotMode == false)
      return;

    int x = e->x();
    int y = e->y();

    double w = (double)width();
    double h = (double)height();
    ratio = (w<h? h / w : w / h);

    switch (e->button())
    {

        case Qt::LeftButton:
            finalizePlot();
            break;

        case Qt::RightButton:

            lastMouseX = x;
            lastMouseY = y;

            if(this->Polygons.empty())
                Polygons.resize(1);


            Point2d pt(((2.0*(double)x)-w)/(w), (1.0-2.0*(double)y/h));
            this->Polygons[0].points.push_back(pt);

            renderlog<<(double)x/width()<<", "<<1.0-(double)y/height()<<"\n";
            break;

     }

    updateGL();

}

void RenderArea::mouseMoveEvent(QMouseEvent *e)
{
//  if (PlotMode == false)
//    return;



 this->setMouseTracking(true);

 this->lastMouseX = e->x();
 this->lastMouseY = e->y();


  updateGL();
}

void RenderArea::forPlotPolygon()
{
    glClearColor(0.0, 0.0, 0.0, 0.0);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    this->PlotMode = true;

    resizeGL(this->width(), this->height());

//    if(!this->Polygons.empty()){
//        for(int k = 0; k < this->Polygons.size(); k++)
//            this->Polygons[k].points.clear();

//    }
    this->Polygons.clear();
    this->Segments.clear();
    this->isShowOriginal = false;
    updateGL();
}


void RenderArea::finalizePlot()
{
    PlotMode = false;
    this->DataReady = true;
    if(!iniPolygon)
        iniPolygon = new MPolygon;
    else{
        iniPolygon->points.clear();
    }

    double xmin, xmax, ymin, ymax;
    xmin = 1e10; xmax = -1e10;
    ymin = 1e10; ymax = -1e10;
    for(int i = 0; i < this->Polygons[0].PointNo(); i ++)
    {
        iniPolygon->points.push_back(Polygons[0].points[i]);
        xmin = std::min(xmin, Polygons[0].points[i].x);
        xmax = std::max(xmax, Polygons[0].points[i].x);
        ymin = std::min(ymin, Polygons[0].points[i].y);
        ymax = std::max(ymax, Polygons[0].points[i].y);
    }

    this->BoundingBox[0][0] = xmin;
    this->BoundingBox[1][0] = xmax;
    this->BoundingBox[0][1] = ymin;
    this->BoundingBox[1][1] = ymax;
    resizeGL(width(),height());
}


bool RenderArea::saveFile(const QString &fileName)
{

    if(Polygons.empty())
        return false;

    std::ofstream fpoly(fileName.toLatin1());
    if (!fpoly.good()) {
        return false;
    }

    fpoly<<"Polygons:\n";
    fpoly.precision(12);
    fpoly.setf(std::ios::fixed,std::ios::floatfield);
    for(int k = 0; k < this->Polygons.size(); k++){
        for(int i = 0; i < this->Polygons[k].PointNo(); i++){
            fpoly<<this->Polygons[k].points[i].x<<"\t"<<this->Polygons[k].points[i].y<<"\n";

        }
        fpoly<<"\n";
    }

    fpoly<<"Segments:\n";
    for(int k = 0; k < this->Segments.size(); k++){
        fpoly<<this->Segments[k].A.x<<" "<<this->Segments[k].A.y<<" "<<this->Segments[k].B.x<<" "<<this->Segments[k].B.y<<"\n";
    }

    return true;
}


void RenderArea::rescale()
{
    if(!this->hasData())
        return;

    double xmin, xmax, ymin, ymax;
    xmin = 1e10; xmax = -1e10;
    ymin = 1e10; ymax = -1e10;
    for(int k = 0; k < this->Polygons.size(); k ++){
        for(int i = 0; i < this->Polygons[k].PointNo(); i ++)
        {
            xmin = std::min(xmin, Polygons[k].points[i].x);
            xmax = std::max(xmax, Polygons[k].points[i].x);
            ymin = std::min(ymin, Polygons[k].points[i].y);
            ymax = std::max(ymax, Polygons[k].points[i].y);
        }
    }

    this->BoundingBox[0][0] = xmin;
    this->BoundingBox[1][0] = xmax;
    this->BoundingBox[0][1] = ymin;
    this->BoundingBox[1][1] = ymax;

    resizeGL(width(),height());
    updateGL();

}


void RenderArea::refine()
{
    if(!this->hasData())
        return;


    this->Polygons[0].subdivision();

    iniPolygon->points.resize(getVerticesNumber(), Point2d());
    for(int i = 0; i < this->getVerticesNumber(); i++)
    {
        iniPolygon->points[i].x = Polygons[0].points[i].x;
        iniPolygon->points[i].y = Polygons[0].points[i].y;
    }

    updateGL();
}
